package a40;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.http11.Http11Nio2Protocol;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;

public class A40 {
    /*
      Server
      └───Service
          ├───Connector (协议, 端口)
          └───Engine
              └───Host(虚拟主机 localhost)
                  ├───Context1 (应用1, 可以设置虚拟路径, / 即 url 起始路径; 项目磁盘路径, 即 docBase )
                  │   │   index.html
                  │   └───WEB-INF
                  │       │   web.xml (servlet, filter, listener) 3.0
                  │       ├───classes (servlet, controller, service ...)
                  │       ├───jsp
                  │       └───lib (第三方 jar 包)
                  └───Context2 (应用2)
                      │   index.html
                      └───WEB-INF
                              web.xml
   */
    public static void main(String[] args) throws IOException, LifecycleException {

        // 1.创建 Tomcat 对象
        Tomcat tomcat = new Tomcat();
        tomcat.setBaseDir("tomcat");

        // 2.创建项目文件夹, 即 docBase 文件夹
        File docBase = Files.createTempDirectory("boot.").toFile();
        docBase.deleteOnExit();

        // 3.创建 Tomcat web项目, 在 Tomcat 中称为 Context
        Context context = tomcat.addContext("", docBase.getAbsolutePath());

        WebApplicationContext springContext = getApplicationContext();


        /**
         * 4.编程添加servlet。3.0后，支持编程添加servlet，而不需要通过web.xml。
         * onStartup会在tomcat启动是被调用，加入servlet
         */

        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> c, ServletContext servletContext) throws ServletException {

                //手动添加servlet
                /**
                HelloServlet helloServlet = new HelloServlet();
                servletContext.addServlet("aaa", helloServlet).addMapping("/");
                 **/


                 //从spring容器中获取servlet，放入context。
                /**
                DispatcherServlet dispatcherServlet = springContext.getBean(DispatcherServlet.class);
                servletContext.addServlet("dispatcherServlet", dispatcherServlet).addMapping("/");
                 */

                //servlet可能不止一个，可以通过ServletRegistrationBean的方式统一注册多个servlet
                for (ServletRegistrationBean registrationBean : springContext.getBeansOfType(ServletRegistrationBean.class).values()) {
                    registrationBean.onStartup(servletContext);
                }


            }
        }, Collections.emptySet());


        // 5.启动 Tomcat
        tomcat.start();

        // 6.创建连接器, 设置监听端口.所谓的默认端口也是通过这个设置的
        Connector connector = new Connector(new Http11Nio2Protocol());
        connector.setPort(8080);
        tomcat.setConnector(connector);
    }

    public static WebApplicationContext getApplicationContext() {
        //AnnotationConfigServletWebServerApplicationContext是包含tomcat服务器的，我们这里手动模拟tomcat，所以不用这个容器
        //AnnotationConfigServletWebServerApplicationContext
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(Config.class);
        context.refresh();
        return context;
    }


    @Configuration
    static class Config {
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        @Bean
        // 这个例子中必须为 DispatcherServlet 提供 AnnotationConfigWebApplicationContext, 否则会选择 XmlWebApplicationContext 实现
        public DispatcherServlet dispatcherServlet(WebApplicationContext applicationContext) {
            return new DispatcherServlet(applicationContext);
        }

        //添加一个json转换器，因为默认的HandlerAdapter是没有的
        @Bean
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
            handlerAdapter.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter()));
            return handlerAdapter;
        }

        @RestController
        static class MyController {
            @GetMapping("hello2")
            public Map<String, Object> hello() {
                return Collections.singletonMap("hello2", "hello2, spring!");
            }
        }
    }
}
